package ltd.newbee.mall.recommend.core;

import com.google.common.collect.Multimap;
import com.google.common.collect.TreeMultimap;
import ltd.newbee.mall.recommend.dto.RelateDTO;

import java.util.*;
import java.util.stream.Collectors;

public class ItemCF {

    /**
     * 基于物品的协同过滤
     *
     * @param userId 用户ID
     * @param num    返回数量（推荐商品数量）
     * @param list   预处理数据（所有用户购买数量多的商品）
     * @return 商品id集合
     * １、预处理数据按照用户和商品分别分组，然后获取当前用户喜欢的商品列表。
     * 2、对于用户喜欢的每个商品，通过CoreMath.computeNeighbor方法计算与该商品关联度最高的其他商品，并将关联度值放入itemTotalDisMap中
     * 3、最后，按关联度值进行降序排序，选择前num个关联度值对应的商品ID，返回推荐的商品ID列表。
     */
    public static List<Long> recommend(Long userId, Integer num, List<RelateDTO> list) {
        // 按用户分组
        Map<Long, List<RelateDTO>> userMap = list.stream()
                .collect(Collectors.groupingBy(RelateDTO::getUserId));
        // 获取当前用户的物品列表
        List<Long> userProductItems = userMap.getOrDefault(userId, Collections.emptyList()).stream()
                .map(RelateDTO::getProductId).toList();
        // 按商品总类分组
        Map<Long, List<RelateDTO>> itemMap = list.stream()
                .collect(Collectors.groupingBy(RelateDTO::getProductId));
        // 存储相似商品的ID列表
        List<Long> similarProductIdList = new ArrayList<>();
        // 存储物品之间的关系值，使用TreeMultimap保持排序
        Multimap<Double, Long> itemTotalDisMap = TreeMultimap.create();
        for (Long itemId : userProductItems) {
            // 获取其他物品与当前物品的关系值
            Map<Double, Long> itemDisMap = CoreMath.computeNeighbor(itemId, itemMap, 1);
            // 将关系值放入总的关系值Map中
            itemDisMap.forEach(itemTotalDisMap::put);
        }

        // 对关系值进行降序排序
        List<Double> values = new ArrayList<>(itemTotalDisMap.keySet());
        values.sort(Collections.reverseOrder());
        // 获取前num个关系值
        List<Double> scoresList = values.stream().limit(num).toList();
        // 获取关系最近的用户
        for (Double aDouble : scoresList) {
            Collection<Long> longs = itemTotalDisMap.get(aDouble);
            for (Long productId : longs) {
                // 如果用户没有喜欢过这个商品，则加入推荐列表
                if (!userProductItems.contains(productId)) {
                    similarProductIdList.add(productId);
                }
            }
        }
        // 返回前num个推荐商品ID，去重
        return similarProductIdList.stream().distinct().limit(num).toList();
    }

}
